unit CPDefaultBuiltinFunctions;

interface

uses CPParserBase, CPBuiltinFunctions;

var
  { List default built-in functions }
  CPDefaultBuiltinFunctionList: TCPBuiltinFunctionList;

implementation

uses Math, CPNoise;

const
  { Degrees per Radiant (equals 180 / Pi) }
  CPDegreesPerRadiant = 180.0 / CPPi;

  { Constant for Sqrt(2 * CPPi) }
  Sqrt_2_X_Pi: TCPFloat = 2.506628274631000502415765284811;

function PerlinNoise1D(X: Double; Persistence: Double; N: Integer): Double;
var
  Frequency, Amplitude: Double;
  I: Integer;
begin
  Result := 0;
  Frequency := 1;
  Amplitude := 1;
  Persistence := -Persistence;
  for I := 1 to N do
  begin
    //Result := Result + Noise(X * Frequency, 0, 0) * Amplitude;
    Result := Result + Noise1(X * Frequency) * Amplitude;
    Frequency := Frequency * 2.0;
    Amplitude := Amplitude * Persistence;
  end;
end;


{ Returns improved perlin noise for Arguments[0], Arguments[1] and Arguments[2] }
function CPNoise(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := PerlinNoise1D(Arguments[0], Arguments[1], Floor(Arguments[2]));
end;

{ Returns the floating point modulo from older Crispy Plotter versions }
//function CPMod(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
//var
//  M: TCPFloat;
//begin
//  M := Arguments[1];
//  Result := Frac(Arguments[0] / M) * M;
//end;

{ Returns sine of Arguments[0] }
function CPSin(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Sin(Arguments[0]);
end;

{ Returns cosine of Arguments[0] }
function CPCos(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Cos(Arguments[0]);
end;

{ Returns tangent of Arguments[0] }
function CPTan(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Tan(Arguments[0]);
end;

{ Returns natural logarithm of Arguments[0] }
function CPLn(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Ln(Arguments[0]);
end;

{ Returns logarithm of Arguments[0] to the base Arguments[1] }
function CPLog(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := LogN(Arguments[0], Arguments[1]);
end;

{ Returns arcus sine of Arguments[0] }
function CPArcSin(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := ArcSin(Arguments[0]);
end;

{ Returns arc cosine of Arguments[0] }
function CPArcCos(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := ArcCos(Arguments[0]);
end;

{ Returns arc tangent of Arguments[0] }
function CPArcTan(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := ArcTan(Arguments[0]);
end;

{ Returns arc tangent of the Arguments[1] / Arguments[0] and returns an angle in the
  correct quadrant }
function CPArcTan2(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := ArcTan2(Arguments[1], Arguments[0]);
end;

{ Returns Arguments[0] raised to the power of the mathematical constant }
function CPExp(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Exp(Arguments[0]);
end;

{ Returns a pseudo random number between 0 and Arguments[0] }
function CPRand(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Random * Arguments[0];
end;

{ Returns the absolute value of Arguments[0] }
function CPAbs(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Abs(Arguments[0]);
end;

{ Returns the square of Arguments[0] }
function CPSqr(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Sqr(Arguments[0]);
end;

{ Returns the square root of Arguments[0] }
function CPSqrt(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Sqrt(Arguments[0]);
end;

{ Returns 1, if Arguments[0] is greater than 0, -1 if Arguments[0] is less than 0 and 0
  if Arguments[0] equals 0 }
function CPSign(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Sign(Arguments[0])
end;

{ Returns the fractional part of Arguments[0] }
function CPFrac(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Frac(Arguments[0])
end;

{ Returns the rounded value of Arguments[0] }
function CPRound(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Round(Arguments[0]);
end;

{ Returns the integral part of Arguments[0] }
function CPTrunc(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Trunc(Arguments[0]);
end;

{ Returns the probability density of Arguments[0] in a normal distribution with
  a mean of Arguments[1] and standard deviation of Arguments[2] }
function CPGauss(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
// Arg 0 is x
// Arg 1 is Mu
// Arg 2 is Sigma
begin
  Result := Exp(-Sqr((Arguments[1] - Arguments[0]) / (2 * Sqr(Arguments[2]))))
    / (Arguments[2] * Sqrt_2_X_Pi);
end;

{ Returns the function value of Arguments[0] for a triangle wave with the frequency
  of Arguments[1] }
function CPTriWav(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  // 1 - 2 * abs(1 - 2 * f * x % 2)
  if Arguments[1] > 0 then
  begin
    if Arguments[0] > 0 then
      Result := 1 - 2 * Abs(1 - Frac(Arguments[1] * Arguments[0] + 0.25) * 2)
    else
      Result := 2 * Abs(1 - Frac(-Arguments[1] * Arguments[0] + 0.25) * 2) - 1;
  end
  else
  begin
    if Arguments[0] > 0 then
      Result := 2 * Abs(1 - Frac(-Arguments[1] * Arguments[0] + 0.25) * 2) - 1
    else
      Result := 1 - 2 * Abs(1 - Frac(Arguments[1] * Arguments[0] + 0.25) * 2);
  end;
end;

{ Returns the function value of Arguments[0] for a square wave with the frequency
  of Arguments[1] }
function CPSquWav(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  if Arguments[1] > 0 then
  begin
    // int(2 * t * f) % 2 * 2 - 1
    if Arguments[0] > 0 then
      Result := -(Trunc(2 * Arguments[1] * Arguments[0]) mod 2 * 2 - 1)
    else
      Result := (Trunc(-2 * Arguments[1] * Arguments[0]) mod 2 * 2 - 1);
  end
  else
  begin
    if Arguments[0] > 0 then
      Result := (Trunc(-2 * Arguments[1] * Arguments[0]) mod 2 * 2 - 1)
    else
      Result := -(Trunc(2 * Arguments[1] * Arguments[0]) mod 2 * 2 - 1);
  end;
end;

{ Returns the function value of Arguments[0] for a sine wave with the frequency
  of Arguments[1] }
function CPSinWav(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Sin(Arguments[0] * 2 * Pi * Arguments[1]);
end;

{ Returns the summed function values of Arguments[3] sine waves using }
function CPSinWaves(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
{
  Arguments[0] - X
  Arguments[1] - Base frequency
  Arguments[2] - Multiplicator
  Arguments[3] - Number of sine waves
}
var
  b: Integer;
begin
  Result := 0;
  For b := 0 to Trunc(Arguments[3]) - 1 do
    Result := Result + Sin
      (Arguments[0] * 2 * Pi * (Arguments[1] + b * Arguments[2]));

end;

{ Returns Arguments[0] converted from radiant to degrees }
function CPDeg(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Arguments[0] * CPDegreesPerRadiant;
end;

{ Returns Arguments[0] converted from degrees to radiant }
function CPRad(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Arguments[0] / CPDegreesPerRadiant;
end;

function CPPoly(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
var
  a: Integer;
begin
  Result := Arguments[ Count - 1];
  for a := Count - 2 downto 1 do
    Result := Result * Arguments[0] + Arguments[a];
end;

function CPCeil(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  if Frac(Arguments[0]) > 0 then
    Result := Trunc(Arguments[0]) + 1
  else
    Result := Trunc(Arguments[0]);
end;

function CPFloor(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  if Frac(Arguments[0]) < 0 then
    Result := Trunc(Arguments[0]) - 1
  else
    Result := Trunc(Arguments[0]);
end;

function CPMin(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Min(Arguments[0], Arguments[1]);
end;

function CPMax(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Max(Arguments[0], Arguments[1]);
end;

function CPClamp(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  if (Arguments[0] < Arguments[1]) then
    Result := Arguments[1]
  else if (Arguments[0] < Arguments[2]) then
    Result := Arguments[0]
  else
    Result := Arguments[2];
end;

function CPStep(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  if (Arguments[1] > Arguments[0]) then
    Result := 1.0
  else
    Result := 0.0;
end;

function CPSmoothStep(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
// x, min, max
// min, max, x
begin
  if (Arguments[2] < Arguments[0]) then
    Result := 0
  else if (Arguments[2] < Arguments[1]) then
  begin
    // Hermite interpolation:
    Result := (Arguments[2] - Arguments[0]) / (Arguments[1] - Arguments[0]);
    Result := (3 - 2 * Result) * Sqr(Result);
  end
  else
    Result := 1;
end;

function CPMix(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := Arguments[0] * (1 - Arguments[2]) + Arguments[1] * Arguments[2];
end;

// New functions for CP 1.7+ sigmoid function sig(x) = 1/(1+e^-x)

function CPSec(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := 1 / Cos(Arguments[0]);
end;

function CPCsc(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := 1 / Sin(Arguments[0]);
end;

function CPCot(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := 1 / Tan(Arguments[0]);
end;

function CPSinH(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := 0.5 * (Exp(Arguments[0]) - Exp(-Arguments[0]));
end;

function CPCosH(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := 0.5 * (Exp(Arguments[0]) + Exp(-Arguments[0]));
end;

function CPTanH(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
var
  Exp2X: TCPFloat;
begin
  Exp2X := Exp(2 * Arguments[0]);
  Result := (Exp2X - 1) / (Exp2X + 1);
end;

function CPSecH(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := 1 / (0.5 * (Exp(Arguments[0]) - Exp(-Arguments[0])));
end;

function CPCscH(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := 1 / (0.5 * (Exp(Arguments[0]) + Exp(-Arguments[0])));
end;

function CPCotH(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
var
  Exp2X: TCPFloat;
begin
  Exp2X := Exp(2 * Arguments[0]);
  Result := (Exp2X + 1) / (Exp2X - 1);
end;

function CPSig(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
begin
  Result := 1 / (1 + Exp(-Arguments[0]));
end;

function CPSawWav(const Arguments: TCPFloatArray; const Count: Integer): TCPFloat;
var
  T: TCPFloat;
begin
  T := Arguments[1] * Arguments[0];
  Result := 2.0 * (T - Round(T));
end;



procedure AddDefaultBuiltinFunctions(BuiltinFunctions: TCPBuiltinFunctionList);

  procedure SetDesc(Name: string; Description:string);
  begin
    BuiltinFunctions.Functions[Name].Description := Description;
  end;

begin
  BuiltinFunctions.Add('Sin', CPSin, 1, 1);
  BuiltinFunctions.Add('Cos', CPCos, 1, 1);
  BuiltinFunctions.Add('Tan', CPTan, 1, 1);
  BuiltinFunctions.Add('Ln', CPLn, 1, 1);
  BuiltinFunctions.Add('Log', CPLog, 2, 2);
  BuiltinFunctions.Add('ArcSin', CPArcSin, 1, 1);
  BuiltinFunctions.Add('ArcCos', CPArcCos, 1, 1);
  BuiltinFunctions.Add('ArcTan', CPArcTan, 1, 1);
  BuiltinFunctions.Add('ArcTan2', CPArcTan2, 2, 2);
  BuiltinFunctions.Add('Exp', CPExp, 1, 1);
  BuiltinFunctions.Add('Rand', CPRand, 1, 1);
  BuiltinFunctions.Add('Abs', CPAbs, 1, 1);
  BuiltinFunctions.Add('Sqr', CPSqr, 1, 1);
  BuiltinFunctions.Add('Sqrt', CPSqrt, 1, 1);
  BuiltinFunctions.Add('Sign', CPSign, 1, 1);
  BuiltinFunctions.Add('Frac', CPFrac, 1, 1);
  BuiltinFunctions.Add('Round', CPRound, 1, 1);
  BuiltinFunctions.Add('Trunc', CPTrunc, 1, 1);
  BuiltinFunctions.Add('Gauss', CPGauss, 3, 3);
  BuiltinFunctions.Add('TriWav', CPTriWav, 2, 2);
  BuiltinFunctions.Add('SquWav', CPSquWav, 2, 2);
  BuiltinFunctions.Add('SinWav', CPSinWav, 2, 2);
  BuiltinFunctions.Add('SinWaves', CPSinWaves, 4, 4);
  BuiltinFunctions.Add('Deg', CPDeg, 1, 1);
  BuiltinFunctions.Add('Rad', CPRad, 1, 1);
  BuiltinFunctions.Add('Poly', CPPoly, 2, 22);
  BuiltinFunctions.Add('Ceil', CPCeil, 1, 1);
  BuiltinFunctions.Add('Floor', CPFloor, 1, 1);
  BuiltinFunctions.Add('Min', CPMin, 2, 2);
  BuiltinFunctions.Add('Max', CPMax, 2, 2);
  BuiltinFunctions.Add('Clamp', CPClamp, 3, 3);
  BuiltinFunctions.Add('Step', CPStep, 2, 2);
  BuiltinFunctions.Add('SmoothStep', CPSmoothStep, 3, 3);
  BuiltinFunctions.Add('Mix', CPMix, 3, 3);
  // New functions added with CP 1.7:
  BuiltinFunctions.Add('Sec', CPSec, 1, 1);
  BuiltinFunctions.Add('Csc', CPCsc, 1, 1);
  BuiltinFunctions.Add('Cot', CPCot, 1, 1);
  BuiltinFunctions.Add('SinH', CPSinH, 1, 1);
  BuiltinFunctions.Add('CosH', CPCosH, 1, 1);
  BuiltinFunctions.Add('TanH', CPTanH, 1, 1);
  BuiltinFunctions.Add('SecH', CPSecH, 1, 1);
  BuiltinFunctions.Add('CscH', CPCscH, 1, 1);
  BuiltinFunctions.Add('CotH', CPCotH, 1, 1);
  BuiltinFunctions.Add('Sig', CPSig, 1, 1);
  BuiltinFunctions.Add('SawWav', CPSawWav, 2, 2);
  BuiltinFunctions.Add('Noise', CPNoise, 3, 3);

  SetDesc('Sin', 'Returns the trigonometric sine of angle (angle in radians)|angle');
  SetDesc('Cos', 'Returns the trigonometric cosine of angle (angle in radians)|angle');
  SetDesc('Tan', 'Returns the trigonometric tangent of angle (angle in radians)|angle');
  SetDesc('Ln', 'Returns the natural logarithm (base e) of x|x');
  SetDesc('Log', 'Returns the logarithm (base b) of x|b|x');
  SetDesc('ArcSin', 'Returns the arc sine of x, in the range of -pi/2 through pi/2|x');
  SetDesc('ArcCos', 'Returns the arc cosine of x, in the range of 0.0 through pi|x');
  SetDesc('ArcTan', 'Returns the arc tangent of x, in the range of -pi/2 through pi/2|x');
  SetDesc('ArcTan2', 'Returns the polar angle of rectangular coordinates (x, y) in the range of -pi through pi|x|y');
  SetDesc('Exp', 'Returns Euler''s number e raised to the power of x|x');
  SetDesc('Rand', 'Returns a pseudorandom value greater than or equal to 0 and less than range|range');
  SetDesc('Abs', 'Returns absolute value of x|x');
  SetDesc('Sqr', 'Returns the square of x|x');
  SetDesc('Sqrt', 'Returns the square root of x|x');
  SetDesc('Sign', 'Returns the sign of x (-1, 0 or 1)|x');
  SetDesc('Frac', 'Returns the fractional part of x|x');
  SetDesc('Round', 'Returns the rounded value of x|x');
  SetDesc('Trunc', 'Returns the value of x without its fractional part|x');
  SetDesc('Gauss', 'Returns the probability density function of the normal distribution with mean mu and standard deviation sigma of x|x|mu|sigma');
  SetDesc('TriWav', 'Returns a sample of a triangle wave with the frequency f at the time x in seconds|x|f');
  SetDesc('SquWav', 'Returns a sample of a square wave with the frequency f at the time x in seconds|x|f');
  SetDesc('SinWav', 'Returns a sample of a sine wave with the frequency f at the time x in seconds|x|f');
  SetDesc('SinWaves', 'Returns a merged sample of count sine wave graphs with a frequency of [index]*mul + f|x|f|mul|count');
  SetDesc('Deg', 'Returns x (given in radians) in degrees|x');
  SetDesc('Rad', 'Returns x (given in degrees) in radians|x');
  SetDesc('Poly', 'Evaluates a uniform polynomial in the form of c0*x^0 + c1*x^1 + c2*x^2 +...|x|c0...c20');
  SetDesc('Ceil', 'Returns a value equal to the nearest integer that is greater than or equal to x|x');
  SetDesc('Floor', 'Returns a value equal to the nearest integer that is less than or equal to x|x');
  SetDesc('Min', 'Returns the argument with the smaller value|value1|value2');
  SetDesc('Max', 'Returns the argument with the bigger value|value1|value2');
  SetDesc('Clamp', 'Returns x in the range of min through max|x|min|max');
  SetDesc('Step', 'Returns 0 if x is smaller or equal to edge, otherwise it returns 1|edge|x');
  SetDesc('SmoothStep', 'Returns 0 if x < min, 1 if x > max and performs smooth Hermite interpolation for values of x between min and max|min|max|x');
  SetDesc('Mix', 'Returns the merged value of src and dest|src|dest|amount');
    // New functions added with CP 1.7:
  SetDesc('Sec', 'Returns the secant of x|x');
  SetDesc('Csc', 'Returns the cosecant of x|x');
  SetDesc('Cot', 'Returns the cotangent of x|x');
  SetDesc('SinH', 'Returns the hyperbolic sine of x|x');
  SetDesc('CosH', 'Returns the hyperbolic cosine of x|x');
  SetDesc('TanH', 'Returns the hyperbolic tangent of x|x');
  SetDesc('SecH', 'Returns the hyperbolic secant of x|x');
  SetDesc('CscH', 'Returns the hyperbolic cosecant of x|x');
  SetDesc('CotH', 'Returns the hyperbolic cotangent of x|x');
  SetDesc('Sig', 'Returns the sigmoid of x|x');
  SetDesc('SawWav', 'Returns a sample of a sawtooth wave with the frequency f at the time x in seconds|x|f');
  // New functions added with CP 1.8:
  SetDesc('Noise', 'Returns a sample of 1D Perlin Noise at the position of x using n octaves and a persistence p|x|p|n');
end;

initialization
  CPDefaultBuiltinFunctionList := TCPBuiltinFunctionList.Create;
  AddDefaultBuiltinFunctions(CPDefaultBuiltinFunctionList);

finalization
  CPDefaultBuiltinFunctionList.Free;
end.
